
/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
// Promise
// 是一个类，new 的时候传入一个执行器函数
// 三种状态，状态只能发生一次改变，初始状态是pending
// 支持链式调用
// then()，传入成功的回调和失败回调，返回的也是一个Promise对象
// Promise.prototype.catch(), 返回一个Promise，并且处理拒绝的情况。
// Promise.all(), 接受一个数组，Promise对象，也可以是普通值，返回一个Promise对象；成功回调中可以拿到入参数组对应的结果，其中有一个入参中的Promise失败，会导致all结果失败
// Promise.any() 接收一个Promise可迭代对象，只要其中的一个 promise 成功，就返回那个已经成功的 promise 。如果可迭代对象中没有一个 promise 成功（即所有的 promises 都失败/拒绝），就返回一个失败的 promise 和AggregateError类型的实例，它是 Error 的一个子类，用于把单一的错误集合在一起。本质上，这个方法和Promise.all()是相反的。
// Promise.race() 方法返回一个 promise，一旦迭代器中的某个promise解决或拒绝，返回的 promise就会解决或拒绝。
// Promise.resolve(value)方法返回一个以给定值解析后的Promise 对象。如果这个值是一个 promise ，那么将返回这个 promise; 否则返回的promise将以此值完成。
// Promise.reject()方法返回一个带有拒绝原因的Promise对象。
// finally() 方法返回一个Promise。在promise结束时，无论结果是fulfilled或者是rejected，都会执行指定的回调函数。
// Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise，并带有一个对象数组，每个对象表示对应的promise结果。

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  status = PENDING
  value = undefined
  reason = undefined
  successCallbacks = []
  failedCallbacks = []
  static resolve(value) {
    return value instanceof MyPromise ? value : new MyPromise((resolve) => resolve(value))
  }
  static reject(reason) {
    // 
    return new MyPromise((resolve, reject) => reject(reason instanceof Error ? reason : reason.toString()))
  }
  static race(array) {
    let res = [];
    let filled = 0;
    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < array.length; i++) {
        if (array[i] instanceof MyPromise) {
          array[i]
            .then(
              data => { 
                resolve(data)
              },
              reason => {
                reject(reason);
              }
            )
        } else {
          resolve(array[i]);
        }
      }
    })
  }
  static all(array) {
    let res = [];
    let filled = 0;
    return new MyPromise((resolve, reject) => {
      function addData(index, value) {
        res[index] = value;
        filled ++;
        if (filled === array.length) {
          resolve(res);
        }
      }
      for (let i = 0; i < array.length; i++) {
        if (array[i] instanceof MyPromise) {
          array[i]
            .then(
              data => addData(i,  data),
              reason => {
                reject(reason);
              }
            )
        } else {
          addData(i, array[i]);
        }
      }
    })
  }
  static any(array) {
    let reasons = [];
    let failed = 0;
    return new MyPromise((resolve, reject) => {
      if (!array.length) reject(new AggregateError({message: 'AggregateError: All promises were rejected', errors: reasons}))
      function addData(index, value) {
        reasons[index] = value;
        failed ++;
        if (failed === array.length) {
          reject(reasons);
        }
      }

      for (let i = 0; i < array.length; i++) {
        if (array[i] instanceof MyPromise) {
          array[i]
            .then(
              data => resolve(data),
              reason => addData(i,  reason)
            )
        } else {
          addData(i, array[i]);
        }
      }
    })
  }
  static allSettled(array) {
    let res = [];
    let settled = 0;
    return new MyPromise((resolve, reject) => {
      function addData(index, value) {
        res[index] = value;
        settled ++;
        if (settled === array.length) {
          resolve(res);
        }
      }
      for (let i = 0; i < array.length; i++) {
        if (array[i] instanceof MyPromise) {
          array[i]
            .then(
              data => addData(i,  data),
              reason => addData(i,  reason)
            )
        } else {
          addData(i, array[i]);
        }
      }
    })
  }
  constructor(executor) {
    try {
      executor(this.resolve, this.reject);
    } catch(e) {
      this.reject(e);
    }
  }
  resolve = (value) => { // 箭头函数：在执行器中调用也可以获得Promise对象上下文
    if (this.status !== PENDING) return; // 状态只能发生一次改变
    this.value = value;
    this.status = FULFILLED;
    while (this.successCallbacks.length) this.successCallbacks.shift()(); // 异步情况下执行所有已注册的成功回调
  }
  reject = (reason) => { // 箭头函数：在执行器中调用也可以获得Promise对象上下文
    if (this.status !== PENDING) return; // 状态只能发生一次改变
    this.reason = reason;
    this.status = REJECTED;
    while (this.failedCallbacks.length) this.failedCallbacks.shift()(); // 异步情况下执行所有已注册的失败回调
  }
  then(successCallback, failedCallback) {
    if (!successCallback) successCallback = value => value;
    if (!failedCallback) failedCallback = reason => { throw reason };
    let newPromise = new MyPromise((resolve, reject) => {
      // 如果当前对象已经有结果，根据状态调用对应的回调
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let cbRes;
            if(successCallback) cbRes = successCallback(this.value)
            resolvePromise(newPromise, cbRes, resolve, reject)
          } catch(e) {
            reject(e);
          }
        }, 0) // 写成异步代码以获取newPromise
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let cbRes;
            if (failedCallback) cbRes = failedCallback(this.reason);
            resolvePromise(newPromise, cbRes, resolve, reject)
          } catch(e) {
            reject(e);
          }
        }, 0) // 写成异步代码以获取newPromise
      } else {
        // 如果当前对象未执行完，是等待状态，存储回调函数，结束后再调用
        if(successCallback) this.successCallbacks.push(() => {
          setTimeout(() => {
            try {
              let cbRes;
              if(successCallback) cbRes = successCallback(this.value)
              resolvePromise(newPromise, cbRes, resolve, reject)
            } catch(e) {
              reject(e);
            }
          }, 0) // 写成异步代码以获取newPromise
        });
        if (failedCallback) this.failedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let cbRes;
              if (failedCallback) cbRes = failedCallback(this.reason);
              resolvePromise(newPromise, cbRes, resolve, reject)
            } catch(e) {
              reject(e);
            }
          }, 0) // 写成异步代码以获取newPromise
        });
      }
    });
    return newPromise;
  }
  catch(failedCallback) {
   return this.then(undefined, failedCallback)
  }
  finally(finallyCallback) {
    this.then(value => {
      return MyPromise.resolve(finallyCallback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(finallyCallback()).then(() => {throw reason})
    })
  }
}

function resolvePromise(newPromise, x, resolve, reject) {
  if (newPromise === x) {
    return reject(new TypeError('Chaning cycle detected for promise'))
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject)
  } else (
    resolve(x)
  )
}

module.exports = MyPromise;
